package com.example.autumn.boot;

import com.example.autumn.io.PropertyResolver;
import com.example.autumn.utils.ClassPathUtils;
import com.example.autumn.web.ContextLoaderInitializer;
import com.example.autumn.web.utils.WebUtils;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Server;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Paths;
import java.util.Set;

/**
 * @author liuzhiyong
 * @date 2023/11/10
 * description: 启动应用
 * 创建Tomcat应用, 初始化IOC容器, 初始化DispatcherServlet, 注册过滤器,注册DispatcherServlet
 * 不使用web模块的ContextLoaderListener监听器, 监听Tomcat启动去做这些初始化的操作, 是因为Tomcat不允许在web.xml中没有声明监听器, 使用没有声明的监听器, 注册Filter和Servlet
 * 使用Boot模块启动, 是不需要在web.xml文件的
 */
public class AutumnApplication {

    final Logger logger = LoggerFactory.getLogger(AutumnApplication.class);


    /**
     * 启动应用
     *
     * @param webDir webapp的目录
     * @param baseDir 资源目录的绝对路径,calss文件
     * @param configClass 启动类的class
     * @param args 参数
     * @author liuzhiyong
     * @date 2023/11/10
     */
    public static void run(String webDir, String baseDir, Class<?> configClass, String... args) throws Exception {
        new AutumnApplication().start(webDir, baseDir, configClass, args);
    }

    public void start(String webDir, String baseDir, Class<?> configClass, String... args) throws Exception {
        printBanner();

        // 启动信息
        final long startTime = System.currentTimeMillis();
        final int javaVersion = Runtime.version().feature();
        final long pid = ManagementFactory.getRuntimeMXBean().getPid();
        final String user = System.getProperty("user.name");
        final String pwd = Paths.get("").toAbsolutePath().toString();
        logger.info("Starting {} using Java {} with PID {} (started by {} in {})", configClass.getSimpleName(), javaVersion, pid, user, pwd);

        // 读取application.yml配置
        PropertyResolver propertyResolver = WebUtils.createPropertyResolver();
        // 创建Tomcat服务器
        Server server = startTomcat(webDir, baseDir, configClass, propertyResolver);

        // 启动信息
        final long endTime = System.currentTimeMillis();
        final String appTime = String.format("%.3f", (endTime - startTime) / 1000.0);
        final String jvmTime = String.format("%.3f", ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0);
        logger.info("Started {} in {} seconds (process running for {})", configClass.getSimpleName(), appTime, jvmTime);

        // 等待服务器结束
        server.await();
    }

    /**
     * 启动嵌入式的Tomcat
     *
     * @param webDir webapp的目录
     * @param baseDir 资源目录的绝对路径,calss文件
     * @param configClass 启动类的class
     * @param propertyResolver 配置文件内容
     * @return {@link Server } Tomcat服务
     * @author liuzhiyong
     * @date 2023/11/10
     */
    protected Server startTomcat(String webDir, String baseDir, Class<?> configClass, PropertyResolver propertyResolver) throws LifecycleException, IOException {
        // 获取配置文件端口
        int port = propertyResolver.getProperty("${server.port:8080}", int.class);
        logger.info("starting Tomcat at port {}...", port);
        // 实例化Tomcat 服务
        Tomcat tomcat = new Tomcat();
        // 设置端口
        tomcat.setPort(port);
        // 设置Connector
        tomcat.getConnector().setThrowOnFailure(true);
        // 添加一个默认的WebApp, 挂在在'/'
        Context ctx = tomcat.addWebapp("", new File(webDir).getAbsolutePath());
        // 设置应用程序的目录
        StandardRoot resources = new StandardRoot(ctx);
        resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/class", new File(baseDir).getAbsolutePath(), "/"));
        ctx.setResources(resources);
        // 设置ServletContainerInitializer监听器
        ctx.addServletContainerInitializer(new ContextLoaderInitializer(configClass, propertyResolver), Set.of());
        // 启动服务器
        tomcat.start();
        logger.info("Tomcat started at port {}...", port);
        return tomcat.getServer();
    }

    protected void printBanner() {
        String banner = ClassPathUtils.readString("/banner.txt");
        banner.lines().forEach(System.out::println);
    }

}
